#pragma once
#include "stdlib/exception.hpp"
using namespace JHCPP::stdlib::jhException;
#include <algorithm>
#include <typeinfo>

namespace JHCPP
{
	namespace utils
	{
		class CAny
		{
		public:
			CAny() : m_content(0)
			{
			}

			template <typename T>
			CAny(const T& value) : m_content(new Holder<T>(value))
			{
			}

			CAny(const CAny& other) : m_content(other.m_content ? other.m_content->clone() : 0)
			{
			}

			~CAny()
			{
				delete m_content;
			}

			CAny& swap(CAny& rhs)
			{
				std::swap(m_content, rhs.m_content);
				return *this;
			}

			template <typename T>
			CAny& operator = (const T& rhs)
			{
				CAny(rhs).swap(*this);
				return *this;
			}

			CAny& operator = (const CAny& rhs)
			{
				CAny(rhs).swap(*this);
				return *this;
			}

			bool empty() const
			{
				return !m_content;
			}

			const std::type_info& type() const
			{
				return m_content ? m_content->type() : typeid(void);
			}

		private:
			class Placeholder
			{
			public:
				virtual ~Placeholder()
				{
				}

				virtual const std::type_info& type() const = 0;
				virtual Placeholder* clone() const = 0;
			};

			template <typename T>
			class Holder: public Placeholder
			{
			public: 
				Holder(const T& value):
				  m_held(value)
				  {
				  }

				  virtual const std::type_info& type() const
				  {
					  return typeid(T);
				  }

				  virtual Placeholder* clone() const
				  {
					  return new Holder(m_held);
				  }

				  T m_held;
			};

		private:
			template <typename T>
			friend T* AnyCast(CAny*);

			template <typename T>
			friend T* UnsafeAnyCast(CAny*);

			Placeholder* m_content;
		};

		template <typename T>
		T* AnyCast(CAny* operand)
		{
			return operand && operand->type() == typeid(T)
				? &static_cast<CAny::Holder<T>*>(operand->m_content)->m_held
				: 0;
		}

		template <typename T>
		const T* AnyCast(const CAny* operand)
		{
			return AnyCast<T>(const_cast<CAny*>(operand));
		}

		template <typename T>
		T AnyCast(const CAny& operand)
		{
			T* result = AnyCast<T>(const_cast<CAny*>(&operand));
			if (!result) throw BadCastException("Failed to convert between const Any types");
			return *result;
		}

		template <typename T>
		T AnyCast(CAny& operand)
		{
			T* result = AnyCast<T>(&operand);
			if (!result) throw BadCastException("Failed to convert between Any types");
			return *result;
		}

		template <typename T>
		const T& RefAnyCast(const CAny & operand)
		{
			T* result = AnyCast<T>(const_cast<CAny*>(&operand));
			if (!result) throw BadCastException("RefAnyCast: Failed to convert between const Any types");
			return *result;
		}

		template <typename T>
		T& RefAnyCast(CAny& operand)
		{
			T* result = AnyCast<T>(&operand);
			if (!result) throw BadCastException("RefAnyCast: Failed to convert between Any types");
			return *result;
		}

		template <typename T>
		T* UnsafeAnyCast(CAny* operand)
		{
			return &static_cast<CAny::Holder<T>*>(operand->m_content)->m_held;
		}

		template <typename T>
		const T* UnsafeAnyCast(const CAny* operand)
		{
			return AnyCast<T>(const_cast<CAny*>(operand));
		}
	}//end of namespace utils
}//end of namespace JHCPP
